#include <fstream>
#include <sstream>
#include "ShaderFactory.hpp"


ShaderFactory::ShaderFactory(void)
{
}


ShaderFactory::~ShaderFactory(void)
{
}

Shader			ShaderFactory::create(const char* vertexSource, const char* fragmentSource) throw (std::exception)
{
	Shader		shader;
	GLint		status;

	if (vertexSource != NULL)
		shader._vertexId = compile(vertexSource, GL_VERTEX_SHADER);
	if (fragmentSource != NULL)
		shader._fragmentId = compile(fragmentSource, GL_FRAGMENT_SHADER);
	shader._programId = glCreateProgram();
	if (vertexSource != NULL)
		glAttachShader(shader._programId, shader._vertexId);
	if (fragmentSource != NULL)
		glAttachShader(shader._programId, shader._fragmentId);
	glLinkProgram(shader._programId);
	glGetProgramiv(shader._programId, GL_LINK_STATUS, &status);
	if (status != GL_TRUE)
	{
		char*				shaderErrorMessage;
		std::stringstream	errorMessage;
		GLint				size;

		glGetProgramiv(shader._programId, GL_INFO_LOG_LENGTH, &size);
		shaderErrorMessage = new char[size + 1];
		glGetShaderInfoLog(shader._programId, size, &size, shaderErrorMessage);
		shaderErrorMessage[size] = 0;
		errorMessage << "Error: ShaderFactory.create: " << shaderErrorMessage;
		delete [] shaderErrorMessage;
#ifdef __MINGW32__
        throw std::exception();
#else
        throw std::exception(errorMessage.str().data());
#endif
	}
	return shader;
}

GLuint			ShaderFactory::compile(const char* source, const GLuint type) throw (std::exception)
{
	GLint		status, size;
	GLuint		shader;
	CharArray	sourceData;

	sourceData = loadSource(source);
	if ((shader = glCreateShader(type)) == 0)
#ifdef __MINGW32__
        throw std::exception();
#else
        throw std::exception("Error: ShaderFactory.compile: Invalid return from glCreateShader.");
#endif
	glShaderSource(shader, 1, const_cast<const GLchar**>(static_cast<GLchar**>(&sourceData.array)), 0);
	delete[] sourceData.array;
	glCompileShader(shader);
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status != GL_TRUE)
	{
		char*				shaderErrorMessage;
		std::stringstream	errorMessage;

		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &size);
		shaderErrorMessage = new char[size + 1];
		glGetShaderInfoLog(shader, size, &size, shaderErrorMessage);
		shaderErrorMessage[size] = 0;
		errorMessage << "Error: ShaderFactory.compile: " << shaderErrorMessage;
		delete [] shaderErrorMessage;
#ifdef __MINGW32__
        throw std::exception();
#else
        throw std::exception(errorMessage.str().data());
#endif
	}
	return (shader);
}

CharArray			ShaderFactory::loadSource(const char* source) throw (std::exception)
{
	std::ifstream	reader;
	CharArray		buffer;

	reader.open(source);
	if (reader.is_open() == false)
	{
		std::stringstream	errorMessage;

		errorMessage << "Error: ShaderFactory.loadSource: Can't open file " << source;
#ifdef __MINGW32__
        throw std::exception();
#else
        throw std::exception(errorMessage.str().data());
#endif
    }
	reader.seekg(0, std::ios::end);
	buffer.length = reader.tellg();
	if (buffer.length == 0)
	{
		std::stringstream	errorMessage;

#ifdef __MINGW32__
        throw std::exception();
#else
        errorMessage << "Error: ShaderFactory.loadSource: File " << source << " is empty.";
#endif
    }
	reader.seekg(0, std::ios::beg);
	buffer.array = new char[buffer.length + 1];
	reader.read(buffer.array, buffer.length);
	reader.close();
	buffer.array[buffer.length] = 0;
	return buffer;
}

void		ShaderFactory::destroy(Shader& shader)
{
	glDeleteShader(shader._fragmentId);
	glDeleteShader(shader._vertexId);
	glDeleteProgram(shader._programId);
}
